package com.wntime.simulator.device;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wntime.simulator.service.SimulationFieldService;
import com.wntime.simulator.service.SimulatorService;
import com.wntime.simulator.utils.LoggerUtils;
import com.wntime.simulator.web.vo.CloneVo;
import com.wntime.simulator.web.vo.SimulatorVo;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class DeviceService {
    private static final Logger log = LoggerFactory.getLogger(DeviceService.class);
//    @Autowired
//    private DB db;

//    @Autowired
//    private LevelDBService levelDBService;

    @Autowired
    private SimulatorService simulatorService;

    @Autowired
    private SimulationFieldService simulationFieldService;

    public static HashMap<String, ModbusDevice> devices = new HashMap<>();


    public Object create(Simulator simulator) throws Exception {

//        if (simulator.getType().equals("modbus")) {
//            List<String> strings = levelDBService.getSims();
//            if (!strings.contains(simulator.getId())) {
//                strings.add(simulator.getId());
//                db.put(Suffix.SIMS.getBytes(UTF_8), JacksonUtils.toJson(strings).getBytes(UTF_8));
//            }
//        }
//        db.put((Suffix.SIMULA + simulator.getId()).getBytes(UTF_8), JacksonUtils.toJson(simulator).getBytes(UTF_8));

        QueryWrapper<com.wntime.simulator.domain.Simulator> wrapper = new QueryWrapper<>();
        wrapper.eq("port", simulator.getPort());
        long count = simulatorService.count(wrapper);
        if (count > 0) {
            return "已经有了这个端口的设备";
        } else {
            com.wntime.simulator.domain.Simulator entity = new com.wntime.simulator.domain.Simulator();
            entity.setPort(simulator.getPort());
            entity.setName(simulator.getName());
            entity.setType(simulator.getType());
            entity.setPort(simulator.getPort());
            entity.setPoolSize(simulator.getPoolSize());
            entity.setOverTcp(simulator.getOverTcp());
            entity.setUintId(simulator.getUintId());
            simulatorService.save(entity);
            simulator.setId(entity.getId().toString());
            initial(simulator);
            return "success";
        }
    }

    public void initial(Simulator simulator) throws Exception {
        ModbusDevice device = new ModbusDevice(simulator);
        device.initial();
        device.start();
        devices.put(simulator.getId(), device);
    }

    public Object delete(SimulatorVo simulator)  {
        String id = simulator.getId();
        ModbusDevice device = devices.get(id);
        if (device != null) {
            device.stop();
            // List<String> sims = levelDBService.getSims();
            // sims.remove(id);
            devices.remove(id);
            // levelDBService.saveSims(sims);

            simulatorService.removeById(Long.parseLong(simulator.getId()));
            QueryWrapper<com.wntime.simulator.domain.SimulationField> wrapper = new QueryWrapper<>();
            wrapper.eq("simulator_id", Long.parseLong(simulator.getId()));
            simulationFieldService.remove(wrapper);
        }
        return "success";
    }

    public Object stop(SimulatorVo simulator) {
        devices.get(simulator.getId()).stop();
        return "ok";
    }

    public Object start(SimulatorVo simulator) {
        devices.get(simulator.getId()).open();
        return "ok";
    }

    public Object updateField(String simulatorId, List<SimulationField> fields) {

        //db.put((Suffix.FIELD + simulatorId).getBytes(UTF_8), JacksonUtils.toJson(fields).getBytes(UTF_8));
        QueryWrapper<com.wntime.simulator.domain.SimulationField> wrapper = new QueryWrapper<>();
        wrapper.eq("simulator_id", Long.parseLong(simulatorId));
        simulationFieldService.remove(wrapper);
        simulationFieldService.saveOrUpdateBatch(toFields(Long.parseLong(simulatorId), fields));
        ModbusDevice device = devices.get(simulatorId);
        device.setSpi(fields);
        return "ok";
    }

    private Collection<com.wntime.simulator.domain.SimulationField> toFields(Long id, List<SimulationField> fields) {
        return fields.stream().map(field -> toField(id, field)).collect(Collectors.toList());
    }

    private com.wntime.simulator.domain.SimulationField toField(Long id, SimulationField field) {
        com.wntime.simulator.domain.SimulationField simulationField = new com.wntime.simulator.domain.SimulationField();
        simulationField.setSimulatorId(id);
        simulationField.setRef(field.getRef());
        simulationField.setName(field.getName());
        simulationField.setBefore(field.getBefore());
        simulationField.setAfter(field.getAfter());
        simulationField.setChange(field.getChange());
        simulationField.setExtendInfo(field.getExtendInfo());
        return simulationField;
    }

//    public void resetField(String simulatorId) throws Exception {
//        List<SimulationField> fields = levelDBService.getFields(simulatorId);
//        ModbusDevice device = devices.get(simulatorId);
//        device.setSpi(fields);
//    }

    public void resetField(Long id, List<SimulationField> fields) {
        ModbusDevice device = devices.get(id + "");
        device.setSpi(fields);
    }

    public Object list() {
        ArrayList<Simulator> objects = new ArrayList<>();
        for (Map.Entry<String, ModbusDevice> entry : devices.entrySet()) {
            Simulator simulator = entry.getValue().getSimulator();
            simulator.setIo(devices.get(simulator.getId()).status());
            objects.add(simulator);
        }
        objects.sort(Comparator.comparing((Simulator::getPort)));
        return objects;
    }

    public HashMap<String, Object> checkPort(SimulatorVo simulator) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("isDuplicate", false);
        for (Map.Entry<String, ModbusDevice> entry : devices.entrySet()) {
//            String k = entry.getKey();
            ModbusDevice v = entry.getValue();
            if (v.getSimulator().getPort().equals(simulator.getPort())) {
                map.put("isDuplicate", true);
                return map;
            }
        }
        return map;
    }

    public HashMap<String, Object> viewField(String simulatorId){
        //List<SimulationField> fields = levelDBService.getFields(simulatorId);
        QueryWrapper<com.wntime.simulator.domain.SimulationField> wrapper = new QueryWrapper<>();
        wrapper.eq("simulator_id", Long.parseLong(simulatorId));
        wrapper.orderByAsc("ref");
        List<com.wntime.simulator.domain.SimulationField> fields = simulationFieldService.list(wrapper);
        HashMap<String, Object> map = new HashMap<>();
        map.put("fields", fields);
        return map;
    }

    /**
     * 复制，未完成
     *
     */
    public Object cloneSimulator(CloneVo clone) {
        if (StringUtils.isNotBlank(clone.getId()) && StringUtils.isNotBlank(clone.getNum()) && Integer.parseInt(clone.getNum()) > 0) {
            com.wntime.simulator.domain.Simulator simulator = simulatorService.getById(Long.parseLong(clone.getId()));
            Integer port = simulatorService.getMaxPort();
            ArrayList<com.wntime.simulator.domain.Simulator> cloneSimulators = new ArrayList<>();
            List<com.wntime.simulator.domain.SimulationField> fields = simulationFieldService.getFieldsBySimulatorId(Long.parseLong(clone.getId()));
            LoggerUtils.printIfInfoEnabled(log, "准备复制 [{}] {} 个，包含字段 {} 个。", simulator.getName(), clone.getNum(), fields.size());
            int a = simulatorService.selectName(simulator.getName());
            for (int i = 0; i < Integer.parseInt(clone.getNum()); i++) {
                com.wntime.simulator.domain.Simulator cloneSimula = getSimulator(simulator, a, i, port);
                simulatorService.save(cloneSimula);
                List<com.wntime.simulator.domain.SimulationField> cloneFields = toCloneFields(cloneSimula.getId(), fields);
                cloneSimulators.add(cloneSimula);
                simulationFieldService.saveBatch(cloneFields);
                LoggerUtils.printIfInfoEnabled(log, "已经复制 [{}] {}个，还差 {} 个。", simulator.getName(), clone.getNum(), fields.size());
            }
            LoggerUtils.printIfInfoEnabled(log, "复制完成 {} 个 {} 模拟设备", simulator.getName(), clone.getNum());
            LoggerUtils.printIfInfoEnabled(log, "启动设备");
            List<SimulationField> simulationFields = fields.stream().map(com.wntime.simulator.domain.SimulationField::toSimulatorField).collect(Collectors.toList());
            for (com.wntime.simulator.domain.Simulator cloneSimulator : cloneSimulators) {
                try {
                    this.initial(cloneSimulator.toSimulator());
                    devices.get(cloneSimulator.getId().toString()).setSpi(simulationFields);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
            return "复制完成";
        }
        return "复制 完成";
    }

    private com.wntime.simulator.domain.Simulator getSimulator(com.wntime.simulator.domain.Simulator simulator, int a, int i, Integer port) {
        com.wntime.simulator.domain.Simulator cloneSimula = new com.wntime.simulator.domain.Simulator();
        cloneSimula.setName(simulator.getName() + "_" + String.format("%04d", a + i));
        cloneSimula.setType(simulator.getType());
        cloneSimula.setPort(port + i + 1);
        cloneSimula.setPoolSize(simulator.getPoolSize());
        cloneSimula.setOverTcp(simulator.getOverTcp());
        cloneSimula.setUintId(simulator.getUintId());
        return cloneSimula;
    }

    private List<com.wntime.simulator.domain.SimulationField> toCloneFields(Long id, List<com.wntime.simulator.domain.SimulationField> fields) {
        List<com.wntime.simulator.domain.SimulationField> cloneFields = new ArrayList<>();
        for (com.wntime.simulator.domain.SimulationField field : fields) {
            com.wntime.simulator.domain.SimulationField cloneField = new com.wntime.simulator.domain.SimulationField();
            cloneField.setSimulatorId(id);
            cloneField.setRef(field.getRef());
            cloneField.setName(field.getName());
            cloneField.setBefore(field.getBefore());
            cloneField.setAfter(field.getAfter());
            cloneField.setChange(field.getChange());
            cloneField.setExtendInfo(field.getExtendInfo());
            cloneFields.add(cloneField);
        }
        return cloneFields;
    }
}
